module("luci.controller.file.upload", package.seeall)

local fs = require "nixio.fs"
local lfs = require "lfs"
local uci = require "luci.model.uci".cursor()
local json = require "cjson.safe"

function index()
	local page = node("file", "upload")
	page.target = firstchild()
	page.title = ("")
	page.order = 100
	page.sysauth = "root"
	page.sysauth_authenticator = "noauth"
	page.index = true

	--文件上传
	entry({"file", "upload", "upload"}, call("upload"), nil)
	--文件列表
	entry({"file", "upload", "fileall"}, call("fileall"), nil)
	--文件列表
	entry({"file", "upload", "list"}, call("filelist"), nil)
	--文件类别大小
	entry({"file", "upload", "storage"}, call("getitemsize"), nil)
	--重命名文件
	entry({"file", "upload", "renameitem"}, call("renameitem"), nil)
	--删除文件
	entry({"file", "upload", "deleteitem"}, call("deleteitem"), nil)
	--远程升级
	entry({"file", "upload", "remoteupgrade"}, call("remote_upgrade"), nil)
	--远程升级
	entry({"file", "upload", "test"}, call("test"), nil)
end

function test()
	local filename = "firmware.bin"
	os.execute("sleep 5 && sysupgrade  /tmp/" .. filename .. " &")
end

function split(str, reps)
	local resultStrList = {}
	string.gsub(
		str,
		"[^" .. reps .. "]+",
		function(w)
			table.insert(resultStrList, w)
		end
	)
	return resultStrList
end

function get_disk_sharesize(path)
	local size = 0
	local share = 0

	uci:foreach(
		"appctrl",
		"appctrl",
		function(s)
			local lcName = s[".name"]

			share = tonumber(uci:get("appctrl", lcName, "share") or 0)
		end
	)
	local fd = io.popen("du -k " .. path .. "/share 2>/dev/null", "r")
	if fd then
		while true do
			local ln = fd:read("*l")
			if not ln then
				break
			else
				if ln:match(path) ~= nil then
					local disk = split(ln, "	")
					size = tonumber(disk[1])
				end
			end
		end
		fd:close()
	end

	if share > size then
		share = share - size
	else
		share = 0
	end

	return share
end

function get_disk_freesize(path, size)
	local share = 0
	local available = 0

	local fd = io.popen("df -k", "r")
	if fd then
		while true do
			local ln = fd:read("*l")
			if not ln then
				break
			else
				if ln:match(path) ~= nil then
					local disk = split(ln, " ")
					available = tonumber(disk[4])
				end
			end
		end
		fd:close()
	end

	share = get_disk_sharesize(path)

	if available - share > size then
		return 1
	else
		return 0
	end
end

function get_curl_result(curl)
	local result = ""
	local fd = io.popen(curl, "r")
	if fd then
		while true do
			local ln = fd:read("*l")
			if not ln then
				break
			else
				result = ln
			end
		end
		fd:close()
	end
	return retsult
end

function get_disk_path(disk)
	local path = "/mnt/mmcblk0p1"

	-- local fd = io.popen("cat /proc/mounts", "r")
	-- if fd then
	-- 	while true do
	-- 		local ln = fd:read("*l")
	-- 		if not ln then
	-- 			break
	-- 		else
	-- 			if ln:match(disk) ~= nil then
	-- 				device, path, fstype = ln:match("^(%S+) (%S+) (%S+)")
	-- 			end
	-- 		end
	-- 	end
	-- 	fd:close()
	-- end
	return path
end

--文件重命名
function renameitem()
	local data = {}
	local srcFilePath = luci.http.formvalue("srcFilePath")
	local dstFilePath = luci.http.formvalue("dstFilePath")

	if srcFilePath ~= nil and dstFilePath ~= nil then
		local rm_file = os.rename(srcFilePath, dstFilePath)
		if rm_file then
			data = {
				bresult = true,
				message = "重命名成功"
			}
		else
			data = {
				bresult = false,
				message = "源文件路径错误或目标文件路径错误"
			}
		end
	else
		data = {
			bresult = false,
			message = "参数格式不正确"
		}
	end
	luci.http.prepare_content("application/json")
	luci.http.write_json(data)
end

--删除
function deleteitem()
	local data = {}
	local filepath = luci.http.formvalue("filePath")
	if filepath ~= nil then
		local rm_file = os.remove(filepath)
		if rm_file then
			data = {
				bresult = true,
				message = "删除成功"
			}
		else
			data = {
				bresult = false,
				message = "文件路径不正确"
			}
		end
	else
		data = {
			bresult = false,
			message = "参数格式不正确"
		}
	end
	luci.http.prepare_content("application/json")
	luci.http.write_json(data)
end

--获取文件夹大小
function getdirsize(path)
	local size = 0
	local fd = io.popen("du -k " .. path, "r")
	if fd then
		while true do
			local ln = fd:read("*l")
			if not ln then
				break
			else
				if ln:match(path) ~= nil then
					local disk = split(ln, "	")
					size = tonumber(disk[1])
				end
			end
		end
		fd:close()
	end
	if size == 16 then
		size = 32
	end
	return size - 32
end

--获取总容量
function getTotalsize()
	local size = 0
	local fd = io.popen("df -k | grep '/dev/mmcblk' | awk '{print $4}'", "r")
	if fd then
		while true do
			local ln = fd:read("*l")
			if not ln then
				break
			else
				size = tonumber(ln)
			end
		end
		fd:close()
	end
	return size
end

function getitemsize()
	local diskpath = get_disk_path("/dev/mmcblk")
	local share = get_disk_sharesize(diskpath)
	local bresult = false
	local message = ""
	local object = {}
	if diskpath then
		photopath = diskpath .. "/photo"
		moviepath = diskpath .. "/movie"
		musicpath = diskpath .. "/music"
		local photosize = getdirsize(photopath)
		local moviesize = getdirsize(moviepath)
		local musicsize = getdirsize(musicpath)
		local totalsize = getTotalsize()
		object["other"] = totalsize - moviesize - photosize - musicsize - share
		object["movie"] = moviesize
		object["photo"] = photosize
		object["music"] = musicsize
		bresult = true
	else
		bresult = false
		message = "无法加载磁盘"
	end
	local data = {
		bresult = bresult,
		object = object,
		message = message
	}
	luci.http.prepare_content("application/json")
	luci.http.write_json(data)
end

--获取扩展名
function get_file_type(str)
	local filetype = ""

	local sion = str:match(".+%.(%w+)$")

	if not sion then
		return "unknow"
	end

	if sion:match("bmp") ~= nil then
		filetype = "photo"
	elseif sion:match("jpg") ~= nil then
		filetype = "photo"
	elseif sion:match("png") ~= nil then
		filetype = "photo"
	elseif sion:match("gif") ~= nil then
		filetype = "photo"
	elseif sion:match("fpx") ~= nil then
		filetype = "photo"
	elseif sion:match("svg") ~= nil then
		filetype = "photo"
	elseif sion:match("psd") ~= nil then
		filetype = "photo"
	elseif sion:match("wmv") ~= nil then
		filetype = "movie"
	elseif sion:match("asf") ~= nil then
		filetype = "movie"
	elseif sion:match("rm") ~= nil then
		filetype = "movie"
	elseif sion:match("rmvb") ~= nil then
		filetype = "movie"
	elseif sion:match("mp4") ~= nil then
		filetype = "movie"
	elseif sion:match("3gp") ~= nil then
		filetype = "movie"
	elseif sion:match("m4v") ~= nil then
		filetype = "movie"
	elseif sion:match("mov") ~= nil then
		filetype = "movie"
	elseif sion:match("avi") ~= nil then
		filetype = "movie"
	elseif sion:match("flv") ~= nil then
		filetype = "movie"
	elseif sion:match("vob") ~= nil then
		filetype = "movie"
	elseif sion:match("mkv") ~= nil then
		filetype = "movie"
	elseif sion:match("asx") ~= nil then
		filetype = "movie"
	elseif sion:match("wav") ~= nil then
		filetype = "music"
	elseif sion:match("aif") ~= nil then
		filetype = "music"
	elseif sion:match("aiff") ~= nil then
		filetype = "music"
	elseif sion:match("au") ~= nil then
		filetype = "music"
	elseif sion:match("mp1") ~= nil then
		filetype = "music"
	elseif sion:match("mp2") ~= nil then
		filetype = "music"
	elseif sion:match("mp3") ~= nil then
		filetype = "music"
	elseif sion:match("wma") ~= nil then
		filetype = "music"
	elseif sion:match("ogg") ~= nil then
		filetype = "music"
	elseif sion:match("m4a") ~= nil then
		filetype = "music"
	else
		filetype = "unknow"
	end

	return filetype
end

-- 文件上传
function upload()
	local data = {}
	local bresult = false
	local flag = 1
	local name = ""
	local filename = ""
	local filetype = ""
	local uploadFile = ""
	local canupload = true
	local msg = "无法识别,拒绝上传"

	-- 获取挂载磁盘路径 --
	local diskpath = get_disk_path("/dev/mmcblk")

	local filesize = tonumber(luci.http.getenv("CONTENT_LENGTH"))

	-- 判断剩余空间 --
	if diskpath and filesize then
		filesize = math.modf(filesize / 1024)
		local ret = get_disk_freesize(diskpath, filesize)
		if ret == 0 then
			canupload = false
			bresult = false
			msg = "磁盘空间不足,拒绝上传"
		end
	else
		canupload = false
		bresult = bresult
		msg = "未检测到磁盘,拒绝上传"
	end

	luci.http.setfilehandler(
		function(meta, chunk, eof)
			if canupload then
				if not fp then
					if meta then
						if flag then
							name = meta.name
							filename = meta.file
							filetype = get_file_type(filename)
							uploadFile = diskpath .. "/" .. filetype .. "/" .. filename

							if filetype == "unknow" then
								canupload = false
								code = 1
								msg = "文件无法识别,拒绝上传"
							end
							fs.unlink(uploadFile)
							flag = 0
						end

						fp = io.open(uploadFile, "w")

					-- if not fp then
					-- 	canupload = false
					-- 	code = 1
					-- 	msg = "磁盘拒绝写入"
					-- end
					end
				end

				if chunk and fp then
					fp:write(chunk)
				end

				if eof and fp then
					fp:close()
					bresult = true
					msg = "上传成功"
				end
			end
		end
	)
	luci.http.formvalue(name)

	if filetype == "photo" and bresult == true then
		curlUpload(uploadFile, filename)
	end
	--os.execute(uploadFile)
	data = {
		bresult = bresult,
		message = msg
	}
	luci.http.prepare_content("application/json")
	luci.http.write_json(data)
end

function file_exists(path)
	local file = io.open(path, "rb")
	if file then
		file:close()
	end
	return file ~= nil
end

function syncToWeb()
end

function fileall()
	-- 获取挂载磁盘路径 --
	local diskpath = get_disk_path("/dev/mmcblk")
	local path = diskpath .. "/photo"
	local photos = getFilesByPath(path, "photo")
	path = diskpath .. "/music"
	local musics = getFilesByPath(path, "music")
	path = diskpath .. "/movie"
	local movies = getFilesByPath(path, "movie")
	local data = {}
	for k, v in pairs(photos) do
		data[#data + 1] = v
	end
	for k, v in pairs(movies) do
		data[#data + 1] = v
	end
	for k, v in pairs(musics) do
		data[#data + 1] = v
	end
	luci.http.prepare_content("application/json")
	luci.http.write_json(data)
end

function getFilesByPath(path, filetype)
	local data = {}
	for file in lfs.dir(path) do
		if file ~= "." and file ~= ".." then
			local filename = file
			local filepath = path .. "/" .. file
			local attr = lfs.attributes(filepath)
			for name, value in pairs(attr) do
				if name == "size" then
					filesize = value
					filesize = math.modf(filesize / 1024)
				end

				if name == "change" then
					filetime = value
				end
			end
			if filesize > 0 then
				data[#data + 1] = {
					fileName = filename,
					fileSize = filesize,
					uploadTime = filetime * 1000,
					filePath = filepath
				}
			end
		end
	end
	if filetype == "photo" then
		local photoMap = {}
		local fileContent = readfile("/mnt/mmcblk0p1/photos.txt")
		if fileContent then
			local photolst = fileSplit(fileContent, ";")
			photoMap = photoTomMap(photolst, "=")
		end
		for key, item in pairs(data) do
			item.fileType = 1
			local urlpath = in_table(photoMap, item.filePath)
			if item.filePath ~= nil then
				if urlpath ~= nil then
					item.url = urlpath
				else
					item.url = "http://192.168.188.1/photo/" .. item.fileName
				end
			end
		end
	elseif filetype == "movie" then
		for key, item in pairs(data) do
			item.fileType = 0
			item.url = ""
		end
	elseif filetype == "music" then
		for key, item in pairs(data) do
			item.fileType = 2
			item.url = ""
		end
	end

	return data
end

-- 文件集合
function filelist()
	local data = nil
	local t_datas = nil
	local bresult = true
	local msg = "无法识别"
	local photoMap = {}
	local filetype = tonumber(luci.http.formvalue("fileType"))
	local count = 0

	if filetype == 0 then
		filetype = "movie"
	elseif filetype == 1 then
		filetype = "photo"
	elseif filetype == 2 then
		filetype = "music"
	else
		filetype = nil
	end

	if filetype == "photo" then
		local fileContent = readfile("/mnt/mmcblk0p1/photos.txt")
		if fileContent then
			local photolst = fileSplit(fileContent, ";")
			photoMap = photoTomMap(photolst, "=")
		end
	end
	-- 获取挂载磁盘路径 --
	local diskpath = get_disk_path("/dev/mmcblk")

	if diskpath and filetype then
		path = diskpath .. "/" .. filetype
		if file_exists(path) then
			for file in lfs.dir(path) do
				if file ~= "." and file ~= ".." then
					if data == nil then
						data = {}
					end
					local filename = file
					local filepath = path .. "/" .. file
					local attr = lfs.attributes(filepath)
					for name, value in pairs(attr) do
						if name == "size" then
							filesize = value
							filesize = math.modf(filesize / 1024)
						end

						if name == "change" then
							filetime = value
						end
					end
					if filesize > 0 then
						count = 1
						data[#data + 1] = {
							fileName = filename,
							fileSize = filesize,
							uploadTime = filetime,
							filePath = filepath
						}
					end
				end
				bresult = true
				msg = "Success"
			end
			if data ~= nil and count == 1 then
				tidy_data_times(data)
				if filetype == "photo" then
					t_datas = {}
					for key, item in pairs(data) do
						xfiletime = getTimeStamp(item.uploadTime)
						local list = in_array(xfiletime, t_datas)
						local urlpath = in_table(photoMap, item.filePath)
						if item.filePath ~= nil then
							if urlpath == nil then
								urlpath = "http://192.168.188.1/photo/" .. item.fileName
							end

							if list then
								list.items[#list.items + 1] = {
									fileName = item.fileName,
									fileSize = item.fileSize,
									uploadTime = item.uploadTime,
									filePath = item.filePath,
									url = urlpath
								}
							else
								list = {}
								list[1] = {
									fileName = item.fileName,
									fileSize = item.fileSize,
									uploadTime = item.uploadTime,
									filePath = item.filePath,
									url = urlpath
								}
								t_datas[#t_datas + 1] = {
									time = xfiletime,
									items = list
								}
							end
						end
					end
					data = {
						object = t_datas,
						bresult = bresult
					}
				else
					data = {
						object = data,
						bresult = bresult
					}
				end
			else
				data = {
					bresult = false,
					message = "没有数据"
				}
			end
		else
			data = {
				bresult = false,
				message = "没有数据"
			}
		end
	elseif not filetype then
		data = {
			object = data,
			bresult = false,
			message = "文件类型无法识别"
		}
	elseif not diskpath then
		data = {
			object = data,
			bresult = false,
			message = "无法识别磁盘"
		}
	end

	luci.http.prepare_content("application/json")
	luci.http.write_json(data)
end

function tidy_data_times(list)
	table.sort(
		list,
		function(a, b)
			return a.uploadTime > b.uploadTime
		end
	)
end

-- 远程升级
function remote_upgrade()
	local data = {}
	local code = 1
	local msg = "下载异常,请稍后再试"
	local md5str = ""

	local url = luci.http.formvalue("url")
	local md5value = luci.http.formvalue("md5")
	local version = luci.http.formvalue("version")
	local ver = uci:get("web", "system", "fwver")

	if version == ver then
		code = 1
		msg = "没有检测到升级固件"
	else
		code = 0
		msg = "检测到升级固件"
		local filename = "firmware.bin"
		local result = os.execute("curl -o /tmp/" .. filename .. " -s  " .. url .. "")
		if result == 0 then
			md5str = luci.sys.exec("md5sum /tmp/" .. filename .. " | awk '{printf $1}'")
			if md5str == md5value then
				local ret = os.execute("sysupgrade -T /tmp/" .. filename)
				if ret ~= 0 then
					code = 1
					msg = "无法识别固件,请稍后再试"
				else
					code = 0
					msg = "路由器在升级过程中请勿断电、刷新或关闭页面,否则会导致升级失败。"
					-- LED灯闪烁 + 升级
					os.execute("sleep 5 && sysupgrade  /tmp/" .. filename .. " &")
				end
			else
				code = 1
				msg = "文件检验异常,请稍后再试"
			end
		else
			code = 1
			msg = "下载异常,请稍后再试"
		end
	end
	data = {
		code = code,
		msg = msg
	}
	luci.http.prepare_content("application/json")
	luci.http.write_json(data)
end

function getTimeStamp(t)
	return os.date("%Y-%m-%d", t)
end

function in_table(table, key)
	if not table then
		return nil
	end
	if table then
		for k, v in pairs(table) do
			if k == key then
				return v
			end
		end
	end
	return nil
end

function in_array(b, list)
	if not list then
		return nil
	end
	if list then
		for k, v in pairs(list) do
			if v["time"] == b then
				return v
			end
		end
	end
end

function photoTomMap(nSplitArray, szSeparator)
	local map = {}
	for k, v in pairs(nSplitArray) do
		local p = fileSplit(v, szSeparator)
		local name = p[1]
		local value = p[2]
		map[name] = value
	end
	return map
end

function fileSplit(szFullString, szSeparator)
	local nFindStartIndex = 1
	local nSplitIndex = 1
	local nSplitArray = {}
	while true do
		local nFindLastIndex = string.find(szFullString, szSeparator, nFindStartIndex)
		if not nFindLastIndex then
			nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, string.len(szFullString))
			break
		end
		nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, nFindLastIndex - 1)
		nFindStartIndex = nFindLastIndex + string.len(szSeparator)
		nSplitIndex = nSplitIndex + 1
	end
	return nSplitArray
end

function writefile(path, content)
	local mode = "a"
	local file = io.open(path, mode)
	if file then
		if file:write(content) == nil then
			return false
		end
		io.close(file)
		return true
	else
		return false
	end
end

function readfile(path)
	local file = io.open(path, "r")
	if file then
		local content = file:read("*a")
		io.close(file)
		return content
	end
	return nil
end

function curlUpload(filepath, filename)
	-- local filepath = "/mnt/mmcblk0p1/photo/wx_camera_1543668198501.jpg"
	-- local filename = "wx_camera_1543668198501.jpg"
	local para = string.format("file=@%s; filename=%s", filepath, filename)
	local result = luci.sys.exec('curl -F "' .. para .. '" https://www.zeerong.com/router/upload/file/upload')
	local jsonobj = json.decode(result)
	if jsonobj then
		local url = jsonobj["url"]
		local content = string.format("%s=%s;", filepath, url)
		writefile("/mnt/mmcblk0p1/photos.txt", content)
	end
end
